package com.dagit.core.pool;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

public abstract class ObjectPool<T> {
	  private long expirationTime;
	  public static int DEFAULT_POOL_SIZE = 10;
	  private int poolSize = DEFAULT_POOL_SIZE;


	  private Map<T, Long> locked, unlocked;

	  public ObjectPool() {
	    expirationTime = 30000; // 30 seconds
	    locked = new HashMap<T, Long>();
	    unlocked = new HashMap<T, Long>();
	  }

	  protected abstract T create() throws CommunicationException;

	  public abstract boolean validate(T o);

	  public abstract void expire(T o) throws CommunicationException;

	  
	  
	  private T getAnyAvail( long now) throws CommunicationException{
		  T t = null;
		  Iterator<T> e = unlocked.keySet().iterator();
	      while (e.hasNext()) {
	        t = e.next();
	        if ((now - unlocked.get(t)) > expirationTime) {
	          // object has expired
	          unlocked.remove(t);
	          expire(t);
	          t = null;
	        } else {
	          if (validate(t)) {
	            unlocked.remove(t);
	            locked.put(t, now);
	            return (t);
	          } else {
	            // object failed validation
	            unlocked.remove(t);
	            expire(t);
	            t = null;
	          }
	        }
	      }
	      return t;
	  }
	  public synchronized T checkOut() throws CommunicationException {
	    long now = System.currentTimeMillis();
	    if (unlocked.size() > 0) {
	    	T t  = getAnyAvail(now);
	      if (t != null){
	    	  return t;
	      }
	    }
	    // no objects available, create a new one
	    if ((unlocked.size() + locked.size()) < poolSize){
		    T t = create();
		    locked.put(t, now);
		    return (t);	    	
	    }else{
	    	throw new CommunicationException("Resource not available");
	    }
	  }

	public synchronized void checkIn(T t) throws CommunicationException {
	    locked.remove(t);
	    unlocked.put(t, System.currentTimeMillis());
	}

	public long getExpirationTime() {
		return expirationTime;
	}

	public void setExpirationTime(long expirationTime) {
		this.expirationTime = expirationTime;
	}
}